﻿using System;
using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;

namespace RayDen.Library.Components.Surface
{
    [Serializable]
    public class MatteMaterial : SurfaceBsdf
    {
        public override BrdfType Type
        {
            get { return BrdfType.Diffuse; }
        }

        public MatteMaterial(RgbSpectrum col)
        {
            Kd = col;
            KdOverPI = Kd * MathLab.INVPI;
        }

        public override bool IsDiffuse() { return true; }
        public override bool IsSpecular() { return false; }

        public override void f(ref Vector wo, ref Vector wi, ref Normal N, ref RgbSpectrum in_fs, out RgbSpectrum fs)
        {
            fs = KdOverPI;
        }

        public override void Sample_f(ref Vector wo, ref Normal N, ref Normal shadeN, ref RgbSpectrum in_f, float u0, float u1, float u2, ref SurfaceTextureData surfaceData, out BsdfSampleData result)
        {
            result.Lambda = 0f;

            Vector dir =
                MC.CosineSampleHemisphere(u0, u1);
            result.Pdf = dir.z * MathLab.INVPI;
            result.Type = BrdfType.Diffuse;
            Vector v1, v2;
            Normal n = N;
            Vector.CoordinateSystem(ref n, out v1, out v2);

            dir = new Vector(
                v1.x * dir.x + v2.x * dir.y + n.x * dir.z,
                v1.y * dir.x + v2.y * dir.y + n.y * dir.z,
                v1.z * dir.x + v2.z * dir.y + n.z * dir.z);

            var wi = dir;

            float dp = (Normal.Dot(ref shadeN, ref wi));
            if (dp <= 0.01f)
            {
                result.Pdf = 0f;
                result.F = new RgbSpectrum();
            }
            else
            {
                result.Pdf /= dp;
                result.F = surfaceData.Diffuse*MathLab.INVPI;
            }
            result.Wi = wi;
        }

        public RgbSpectrum GetKd() { return Kd; }

        private RgbSpectrum Kd;
        private RgbSpectrum KdOverPI;
    };


    [Serializable]
    public class SpectralMatteMaterial : SurfaceBsdf
    {
        public override BrdfType Type
        {
            get { return BrdfType.Diffuse; }
        }

        public SpectralMatteMaterial(SampledSpectrum col)
        {
            Kd = col;
            KdOverPI = Kd * MathLab.INVPI;
        }

        public override bool IsDiffuse() { return true; }
        public override bool IsSpecular() { return false; }

        public override void f(ref Vector wo, ref Vector wi, ref Normal N, ref RgbSpectrum in_fs, out RgbSpectrum fs)
        {
            fs = KdOverPI.ToRgb();
        }

        public override void Sample_f(ref Vector wo, ref Normal N, ref Normal shadeN, ref RgbSpectrum in_f, float u0, float u1, float u2, ref SurfaceTextureData surfaceData, out BsdfSampleData result)
        {
            result.Lambda = 0f;

            Vector dir =
                MC.CosineSampleHemisphere(u0, u1);
            result.Pdf = dir.z * MathLab.INVPI;
            result.Type = BrdfType.Diffuse;
            Vector v1, v2;
            Normal n = N;
            Vector.CoordinateSystem(ref n, out v1, out v2);

            dir = new Vector(
                v1.x * dir.x + v2.x * dir.y + n.x * dir.z,
                v1.y * dir.x + v2.y * dir.y + n.y * dir.z,
                v1.z * dir.x + v2.z * dir.y + n.z * dir.z);

            var wi = dir;

            float dp = (Normal.Dot(ref shadeN, ref wi));
            // Using 0.01 instead of 0.0 to cut down fireflies
            if (dp <= 0.0001f)
            {
                result.Pdf = 0f;
                result.F = new RgbSpectrum();
            }
            else
            {
                result.Pdf /= dp;
                result.F = (KdOverPI).ToRgb() * surfaceData.Diffuse;
            }
            result.Wi = wi;
        }

        public SampledSpectrum GetKd() { return Kd; }

        private SampledSpectrum Kd;
        private SampledSpectrum KdOverPI;
    };
}